<?php
namespace Module\ExtensionLibServer;




use \com\peecfw\module\Module,
	\com\peecfw\module\ModuleTool,
	\com\peecfw\module\ModuleMsg,
	\com\peecfw\module\ModuleValidator,
	\com\peecfw\module\ModuleErrorMsgException,
	\com\peecfw\Url,
	\com\peecfw\DirectoryTools,
	Doctrine,
	Doctrine_Query,
	User,
	Exception,
	\com\peecfw\ApplicationStub,
	\com\peecfw\Post,
	\com\peecfw\Files;
/**
 * PeecFW.ORG's official ExtensionLibServer, it contains modules that are downloadable.
 * It also has an API.
 * This is bundeled with PeecFW because someone might need it.
 * @author Petter Kjelkenes <kjelkenes@gmail.com>
 * @ModuleAnnotation(api = 2.0, version = 4.0, author = "Petter Kjelkenes <kjelkenes@gmail.com>", reqModules = {'Authentication','RateIt'})
 */
class ExtensionLibServer extends Module{
	
	
	
	
	public function run(){
		$this->registerLinkMethod(2, 'Category', 'Category');
		$this->registerLinkMethod(2, 'Module', 'Module');
		$this->registerLinkMethod(2, 'Upload', 'Upload');
		$this->registerLinkMethod(2, 'Rest', 'Rest');
		
		
		
		$categories = $this->getCategories();
		
		$this->template->assign('categories', $categories);		
		$this->setTPL('tpl/categories.tpl');
	}
	
	public function getCategories(){
		// Get modules in category:
		$q = Doctrine_Query::create()
		->select('c.id, c.urlbit, c.description, c.img, c.name')
		->from('Modulelibrarycategory c');
		
		$categories = $q->execute(array(), Doctrine::HYDRATE_ARRAY);
		return $categories;		
	}
	
	public function Category(){
		$c = Url::getBit(3);
		
		
		$modules = $this->getModulesInCategory($c);
		$this->template->assign('modules', $modules);
		$this->setTPL('tpl/modules.tpl');
	}
	
	
	public function langGetExplaination(){
		return $this->getLang('extensionWelcome',array('restAPILink' => $this->linkTo('Rest'), 'uploadModuleLink' => $this->linkTo('Upload')));
	}
	
	/**
	 * 
	 * Identity of the category.
	 * @param string $urlbit
	 */
	public function getModulesInCategory($urlbit){
		// Get modules in category:
		$q = Doctrine_Query::create()
		->select('m.id, m.moduleReadableName, m.description, m.version, u.username, m.moduleName')
		->from('Modulelibrary m')
		->innerJoin('m.Modulelibrarycategory c')
		->leftJoin('m.User u')
		->where('c.urlbit = ?', $urlbit);
		
		$modules = $q->execute(array(), Doctrine::HYDRATE_ARRAY);
		return $modules;
	}

	
	public function Upload(){
		if (!User::isLoggedIn()){
			new ModuleMsg("Uploading a new extension requires Login.");
			ApplicationStub::redirect('Authentication/Login');
		}
		$this->setTPL('tpl/upload.tpl');
		
		
		// Get users own module.
		$q = Doctrine_Query::create()
		->select('m.id, m.moduleReadableName, m.description, m.version, m.moduleName, u.id, u.username')
		->from('Modulelibrary m')
		->innerJoin('m.User u')
		->where('u.id = ?', ApplicationStub::getClientUser()->id);
		$ownmodules = $q->execute(array(), Doctrine::HYDRATE_ARRAY);
		$this->template->assign('ownmodules', $ownmodules);	
		
		$postactive = Post::isActive();
		
		// EDITING
		$editid = (int)Url::getBit(3);
		if ($editid > 0){
			$q = Doctrine_Query::create()
			->select('m.id, m.moduleReadableName, m.description, m.version, m.moduleName, u.id, u.username, c.id')
			->from('Modulelibrary m')
			->innerJoin('m.User u')
			->innerJoin('m.Modulelibrarycategory c')
			->where('m.id = ?', $editid)
			->andWhere('u.id = ?', ApplicationStub::getClientUser()->id);
			
			$row = $q->execute()->getFirst();
			if ($row){
				Post::set('readablename', $row->moduleReadableName);
				Post::set('classname', $row->moduleName);
				Post::set('description', $row->description);
				Post::set('cat_id', $row->Modulelibrarycategory->id);
				Post::set('version', $row->version);
			}
		}
		
		// POST HANDLE.
		if ($postactive){
			$extdir = DirectoryTools::getCachePath().DIRECTORY_SEPARATOR.'extensions'.DIRECTORY_SEPARATOR;
			
			// Flag for , IS THIS A UPDATE?
			$update = false;
			
			// Validation rules for posts.
			if (!preg_match('#[0-9]{0,10}\.[0-9]{0,10}#', Post::get('version')))throw new ModuleErrorMsgException("Version format should be formated to X.X");
			if (!preg_match('#[A-Za-z_]#', Post::get('classname')))throw new ModuleErrorMsgException("Module Class should contain A-Z, a-z and can also contain underscore.");
			if (Post::get('readablename') == '' || strlen(Post::get('readablename')) > 100)throw new  ModuleErrorMsgException("Module Name can not be empty and only contain maximum of 100 characters.");
			if (Post::get('description') == '' || strlen(Post::get('description')) < 100)throw new  ModuleErrorMsgException("Description must be atleast 100 characters long. Please provide a good description for your module.");
			if ((int)Post::get('cat_id') == 0)throw new ModuleErrorMsgException("No such category in the category listing."); // Should not really happen, but for security we have it.
			
			// Validate the correctness of the filename / Module Class.
			if (Post::get('classname').'.zip' != basename($_FILES['uploadedfile']['name']))throw new ModuleErrorMsgException("Name of zip file uploaded seems to be incorrect. According to the provided Module Class provided, your zip file should be named: ".Post::get('classname').".zip");
			
			
			
			$q = Doctrine_Query::create()
			->select('m.id, m.moduleReadableName, m.description, m.version, m.moduleName, u.id, u.username')
			->from('Modulelibrary m')
			->innerJoin('m.User u')
			->where('m.moduleName = ?', Post::get('classname'));
			$row = $q->execute()->getFirst();
			
			if ($row){
				if(ApplicationStub::getClientUser()->id == $row['User']['id']){
					// This is OK, Why? Because it's the user that uploaded the module, he can now proceed.
					$update = true;
				}else{
					throw new ModuleErrorMsgException("Module already exist. Please rename your module.");
				}
			}
			
			
			// Lets try uploading the file
			try{
				$file = new Files($_FILES['uploadedfile']);
				$file->setRule(Files::VALID_MAX_SIZE, 10000000);
				$file->setRule(Files::VALID_FILE_EXTENSION, 'zip');
				$file->setRule(Files::VALID_FILE_TYPE, 'application/zip');
				$file->setRule(Files::VALID_REGEXP_FILENAME, array('#[A-Za-z_]\.zip#', '#'.Post::get('classname').'\.zip#'));
				$uploadedfile = $file->upload($extdir);
			}catch(Exception $e){
				throw new ModuleErrorMsgException($e->getMessage());
			}
			
			// Now unzip the module and go for validation.
			$zip = new \ZipArchive;
			$res = $zip->open($uploadedfile['filepath']);
			if ($res === true) {
				$d = $extdir.time();
				
				mkdir($d);
				$zip->extractTo($d);
				$zip->close();
				
				// Try validation.
				try{
					$validator = new ModuleValidator(Post::get('classname'));
					
					$validator->validate($d);
					
				}catch(Exception $e){
					DirectoryTools::removeDirectoryRecursive($d);
					throw new ModuleErrorMsgException($e->getMessage());
				}
			}else throw new ModuleErrorMsgException("Unable to validate your module. Modules must come in a zip file named by ClassName.zip");
			DirectoryTools::removeDirectoryRecursive($d);
			
			// Now the module is validated. Process ...
			if (!$update)$m = new \Modulelibrary();
			else $m = $row;
			$m->user_id = ApplicationStub::getClientUser()->id;
			$m->moduleName = Post::get('classname');
			$m->moduleReadableName = Post::get('readablename');
			$m->version = Post::get('version');
			
			
			$config = \HTMLPurifier_Config::createDefault();
			$config->set('HTML.Doctype', \com\peecfw\Template::getMeta('Doctype'));
			$config->set('HTML.Allowed','p,h3,h4');
			$m->description = Post::get('description', $config);
			$m->modulelibrarycategory_id = Post::get('cat_id');
			$m->save();
			
			new ModuleMsg("Your module has been validated and is now uploaded, you will be able to see your own module in our repository soon. Thanks for supporting PeecFW and our community.");
		}
		
		
	}
	
	public function Module(){
		$m = (int)Url::getBit(3);
		
		$q = Doctrine_Query::create()
		->select('m.id, m.moduleReadableName, m.description, m.version, m.moduleName')
		->from('Modulelibrary m')
		->where('m.id = ?', $m);
		
		$mod = $q->execute(array(), Doctrine::HYDRATE_ARRAY);
		$this->template->assign('extMod', $mod[0]);
		
		$this->setTPL('tpl/module.tpl');
	}
	
	public function Rest(){
		$r = new ExtensionLibRestServer();
		$r->run();
	}
	
	public function getDLLink($modulename){
		return Url::getHttpDomain().Url::getBaseUrl().'Application/cache/extensions/'.$modulename.'.zip';
	}
	
	public function initModuleRate($id){
		return $this->Mod('RateIt')->registerRating($this, 'ExtensionModule_'.$id);
	}
	public function initCommentForModule($id){
		return $this->Mod('CommentIt')->registerComment($this, 'ExtensionModule_'.$id);
	}
	public function install(){
		
		$c = new \Modulelibrarycategory();
		$c->urlbit = 'Access-And-Security';
		$c->name = 'Access and Security';
		$c->description = 'Authentication based, and security modules and libraries.';
		$c->save();
		
		$c = new \Modulelibrarycategory();
		$c->urlbit = 'Administration';
		$c->name = 'Administration';
		$c->description = 'Administrative modules, modules that contain tools for administrators.';
		$c->save();		
		
		$c = new \Modulelibrarycategory();
		$c->urlbit = 'Ads-And-Affiliates';
		$c->name = 'Ads and Affiliates';
		$c->description = 'Ad modules and affiliate modules.';
		$c->save();		
		
		$c = new \Modulelibrarycategory();
		$c->urlbit = 'Calendars-And-Events';
		$c->name = 'Calendars and Events';
		$c->description = 'Calendars and event modules that allows interactions with calendars and event social modules.';
		$c->save();		
		
		
		$c = new \Modulelibrarycategory();
		$c->urlbit = 'Forums';
		$c->name = 'Forums';
		$c->description = 'Forums / Community boards and extensions for existing boards.';
		$c->save();		
		
		$c = new \Modulelibrarycategory();
		$c->urlbit = 'News-Posting';
		$c->name = 'News Posting';
		$c->description = 'News Posting modules.';
		$c->save();		
		
		$c = new \Modulelibrarycategory();
		$c->urlbit = 'Blogs';
		$c->name = 'Blogs';
		$c->description = 'Blog posting modules so PeecFW grants access to creating and managing blogs / blog posts.';
		$c->save();		
		
		
		$c = new \Modulelibrarycategory();
		$c->urlbit = 'Libraries';
		$c->name = 'Libraries';
		$c->description = 'Libraries that can be used by other modules. Some libraries might be needed for some modules. Libraries is created to generalize applications.';
		$c->save();		
		
		$c = new \Modulelibrarycategory();
		$c->urlbit = 'Other';
		$c->name = 'Other';
		$c->description = 'Modules that does not fit to any of our other categories goes into the other section.';
		$c->save();		
		
		
		// Create dirs.
		$p = DirectoryTools::getCachePath().DIRECTORY_SEPARATOR.'extensions';
		if (!file_exists($p))mkdir($p);
		
	}
	
	
	
}


class ExtensionLibRestServer extends \com\peecfw\Rest{
	
	public function getVersion(){
		return 1.0;
	}
	
	public function init(){
		$this->addMethod('search');
		$this->addMethod('getModule');
		$this->addMethod('getCategories');
		$this->addMethod('getModulesInCategory');
	}
	
	

	/**
	 * 
	 * Searches the Module extension database for a module, $searchString must be provided in order for it to work.
	 * It will search in title and description of each module found in the PeecFW extension library.
	 * @param string $searchString
	 * @param int $limit
	 * @throws Exception
	 */
	public function search($searchString, $limit = 20){
		if ($limit > 20)throw new Exception("\$limit can be maximum of 20.");
		
		$q = Doctrine_Query::create()
		->select('m.id, m.moduleReadableName, m.description, m.version, m.moduleName')
		->from('Modulelibrary m')
		->where('m.moduleReadableName LIKE ?', '%'.$searchString.'%')
		->orWhere('m.description LIKE ?', '%'.$searchString.'%')
		->limit($limit);
		
		$rows = $q->execute(array(), Doctrine::HYDRATE_ARRAY);
		
		foreach($rows as $k => $row){
			$rows[$k]['downloadLink'] = ModuleTool::get('ExtensionLibServer')->getDLLink($row['moduleName']);
		}
		return $rows;
	}
	
	/**
	 * 
	 * Get's a specific module, information and downloadable link.
	 * @param int $moduleID
	 */
	public function getModule($moduleID){
		$q = Doctrine_Query::create()
		->select('m.id, m.moduleReadableName, m.description, m.version, m.moduleName')
		->from('Modulelibrary m')
		->where('m.id = ?', $moduleID);
		
		$rows = $q->execute(array(), Doctrine::HYDRATE_ARRAY);
		
		foreach($rows as $k => $row){
			$rows[$k]['downloadLink'] = ModuleTool::get('ExtensionLibServer')->getDLLink($row['moduleName']);
		}
		return $rows;
	}
	
	/**
	 * 
	 * Get's all the module categories.
	 */
	public function getCategories(){
		return ModuleTool::get('ExtensionLibServer')->getCategories();
	}
	
	/**
	 * 
	 * Get's all modules in specific categories. Use $urlbit return from getCategories() as the parameter.
	 * @param string $urlbit The url bit of the category
	 * @throws Exception
	 */
	public function getModulesInCategory($urlbit){
		return ModuleTool::get('ExtensionLibServer')->getModulesInCategory($urlbit);
	}
	
}
